#pragma once
#include <cstdlib>
#include <cassert>
#include <string>

namespace ECS
{
	template <typename T>
	class TableArray
	{
	public:
		TableArray();
		TableArray(const TableArray<T>& other);
		~TableArray();

		void resize(int width, int height);
		void reserve(int width, int height); 

		const T& at(int x, int y) const;
		void set(int x, int y, const T& value);
		T& get(int x, int y) const;

		int getWidth() const;
		int getHeight() const;
	protected:
		static void copyData(T* source, int swidth, int sheight, T* dest, int dwidth, int dheight);

		int _width;
		int _height;
		int _wcapacity;
		int _hcapacity;

		T* _data;
	};

	template <typename T>
	int TableArray<T>::getWidth() const
	{
		return _width;
	}

	template <typename T>
	int TableArray<T>::getHeight() const
	{
		return _height;
	}

	template <typename T>
	void TableArray<T>::copyData( T* source, int swidth, int sheight, T* dest, int dwidth, int dheight )
	{
		assert(source != nullptr && dest != nullptr && sheight >= 0 && swidth >= 0 && dheight >= 0 && dwidth >= 0);

		int sw = swidth < dwidth ? swidth : dwidth;
		int sh = sheight < dheight ? sheight : dheight;

		for (int i = 0; i < sh; ++i)
		{
			memcpy(dest + dwidth * i, source + swidth * i, sizeof(T) * sw);
		}
	}

	template <typename T>
	TableArray<T>::TableArray() :
		_data(nullptr),
		_width(0),
		_height(0),
		_wcapacity(0),
		_hcapacity(0)
	{

	}

	template <typename T>
	TableArray<T>::TableArray( const TableArray<T>& other ) :
		_width(other._width),
		_height(other._height),
		_wcapacity(other._wcapacity),
		_hcapacity(other._hcapacity)
	{
		if (other._data != nullptr)
		{
			_data = (T*)malloc(sizeof(T) * _wcapacity * _hcapacity);
			memcpy(_data, other._data, sizeof(T) * _wcapacity * _hcapacity);
		}
		else
		{
			_data = nullptr;
		}
	}

	template <typename T>
	TableArray<T>::~TableArray()
	{
		if (_data)
		{
			free(_data);
			_data = nullptr;
		}
	}

	template <typename T>
	void TableArray<T>::resize(int width, int height)
	{
		if (width > _wcapacity || height > _hcapacity)
		{
			reserve(width, height);
		}

		_width = width;
		_height = height;
	}

	template <typename T>
	void TableArray<T>::reserve(int width, int height)
	{
		T* temp = (T*)calloc(width * height, sizeof(T));
		if (_data != nullptr)
		{
			copyData(_data, _width, _height, temp, width, height);
			free(_data);
		}
		_data = temp;
		_wcapacity = width;
		_hcapacity = height;
	}

	template <typename T>
	const T& TableArray<T>::at(int x, int y) const
	{
		assert(x < _width && y < _height && x >= 0 && y >= 0);
		return *(_data + _wcapacity * y + x);
	}

	template <typename T>
	T& TableArray<T>::get(int x, int y) const
	{
		assert(x < _width && y < _height && x >= 0 && y >= 0);
		return *(_data + _wcapacity * y + x);
	}

	template <typename T>
	void TableArray<T>::set(int x, int y, const T& value)
	{
		assert(x < _width && y < _height && x >= 0 && y >= 0);
		*(_data + _wcapacity * y + x) = value;
	}
}